package okw.communication;

import okw.packager.PackagerService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import core.communication.message.ConfigurationMessage;
import core.communication.message.Direction;
import core.communication.message.Message;
import core.communication.message.MessageType;
import core.communication.message.NodeStateEnum;

@Component
@Scope("prototype")
public class MessageReceiver implements ApplicationContextAware, Runnable {

	private static final Logger L = LoggerFactory.getLogger(MessageReceiver.class);

	private ApplicationContext context;

	private volatile Thread thread = null;
	private CommunicationService communicationService;
	@Autowired
	private PackagerService packagerService;

	public MessageReceiver() {
		L.debug("Tworzę MessageReceiver");

		thread = new Thread(this, "sender");

		L.debug("Utworzono MessageReceiver");
	}

	public void stop() {
		L.debug("Stop: {} {}", thread.getName(), thread.getId());

		thread = null;
		wakeUp();
	}

	public synchronized void wakeUp() {
		L.debug("Wstaje: MessageReceiver");

		notify();
	}

	@Override
	public void run() {
		communicationService = context.getBean(CommunicationService.class);
		packagerService.setCommunicationService(communicationService);
		Message message = null;
		while (thread != null) {
			message = null;
			// Pobieramy wiadomość do obsłużenia
			L.debug("Pobieram wiadomość");
			message = communicationService.getReceivedMessage();
			// Jeżeli nie ma wiadomości to usypiamy wątek
			if (message == null) {
				synchronized (this) {
					try {
						if (thread == null)
							return;
						L.debug("Nie ma wiadomości, usypiam");
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				// Obsługujemy wiadomość
			} else {
				L.debug("Obsługuję {}", message.getMessageType());

				if (message.getMessageType() == MessageType.Configuration) {
					communicationService.handleConfiguration(((ConfigurationMessage) message).getConfigurationNodes());
				} else if (message.getMessageType() == MessageType.VotesPackage || message.getMessageType() == MessageType.VotersPackage) {
					packagerService.handlePackage(message);
				} else if (message.getMessageType() == MessageType.CommunicationTest) {
					if (message.getDirection() == Direction.DOWN)
						communicationService.sendNodeStateMessage(message.getDestinationURI(), NodeStateEnum.ON);
				}

				// Usuwamy obsłużoną wiadomość
				communicationService.removeReceivedMessage(message);
			}
		}
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.context = applicationContext;
	}
}
